// Yecc.h
#ifndef YECC
#define YECC

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <signal.h>
#include <execinfo.h>
#include <assert.h>

void print_stacktrace();
#undef assert
#define my_assert(expr) do {                          \
    if (!(expr)) {                                   \
        fprintf(stderr, "Assertion failed: %s (%s:%d)\n", \
                #expr, __FILE__, __LINE__);           \
        print_stacktrace();                           \
        abort();                                      \
    }                                                 \
} while (0)
#define assert(x) my_assert(x)

enum TokenKind {
    TK_HEAD,
    TK_KEYWORD, // if, else, while, return, int ...
    TK_PUNCT,   // +, -, *, /, =, ( ), { }, ;, -> ...
    TK_IDENT,   // var, func
    TK_NUM,     // numeric literal
    TK_CH,      // character literal
    TK_STR,     // string literal
    TK_EOF,     // end-of-file
    TK_UNKNOWN
};

enum KeywordKind {
    // Types
    KW_VOID,
    KW_BOOL,
    KW_CHAR,
    KW_SHORT,
    KW_INT,
    KW_LONG,
    KW_ENUM,
    KW_STRUCT,
    // Attribute of types
    KW_CONST,
    KW_SIGNED,
    KW_UNSIGNED,
    KW_SIZEOF,
    // Branch
    KW_IF,
    KW_ELSE,
    KW_SWITCH,
    KW_CASE,
    KW_DEFAULT,
    // Loop
    KW_DO,
    KW_FOR,
    KW_WHILE,
    KW_BREAK,
    KW_CONTINUE,
    // Others
    KW_RETURN,
    KW_ASM,
    KW_TRUE,
    KW_FALSE,
    KW_NULL,
    // default, if not TK_KEYWORD
    KW_UNKNOWN 
};

enum PunctKind{
    // Arithmetic
    PK_ADD,       // "+"
    PK_SUB,       // "-"
    PK_MUL,       // "*"
    PK_DIV,       // "/"
    PK_MOD,       // "%"
    // Bitwise
    PK_BITAND,    // "&"
    PK_BITOR,     // "|"
    PK_BITXOR,    // "^"
    PK_BITNOT,    // "~"
    // Shift
    PK_SHL,       // "<<"
    PK_SHR,       // ">>"
    // Assign
    PK_ASSIGN,    // "="
    PK_ADD_ASG,   // "+="
    PK_SUB_ASG,   // "-="
    PK_MUL_ASG,   // "*="
    PK_DIV_ASG,   // "/="
    PK_MOD_ASG,   // "%="
    PK_SHL_ASG,   // "<<="
    PK_SHR_ASG,   // ">>="
    PK_AND_ASG,   // "&="
    PK_OR_ASG,    // "|="
    PK_XOR_ASG,   // "^="
    // Logic
    PK_LOG_AND,   // "&&"
    PK_LOG_OR,    // "||"
    PK_LOG_NOT,   // "!"
    // compare
    PK_EQ,        // "=="
    PK_NEQ,       // "!="
    PK_LT,        // "<"
    PK_LE,        // "<="
    PK_GT,        // ">"
    PK_GE,        // ">="
    // Inc and Dec
    PK_INC,       // "++"
    PK_DEC,       // "--"
    // Struct member assess
    PK_DOT,       // "."
    PK_ARROW,     // "->"
    // Brackets
    PK_LPAREN,    // "("
    PK_RPAREN,    // ")"
    PK_LBRACK,    // "["
    PK_RBRACK,    // "]"
    PK_LBRACE,    // "{"
    PK_RBRACE,    // "}"
    // Others
    PK_COMMA,     // ","
    PK_SEMI,      // ";"
    PK_COLON,     // ":"
    PK_QUESTION,  // "?"
    PK_ELLIPSIS,  // "..."
    // default, if not TK_PUNCT
    PK_UNKNOWN
};

struct KeywordInfo {
    const char* text;
    enum KeywordKind kw;
};

struct PunctInfo {
    const char* text;
    enum PunctKind pk;
};

struct Token {
    enum TokenKind kind;
    enum KeywordKind kw; // if and only if TK_KEYWORD
    enum PunctKind pk;   // if and only if TK_PUNCT
    long num;            // if and only if TK_NUM
    char ch;             // if and only if TK_CH
    char* str;           // if and only if TK_STR
    char* name;          // if and only if TK_IDENT
    struct Token* next;
};

enum NodeKind {

    ////////////
    // Operation
    // NodeInfo: left, right
    ////////////

    // Arithmetic
    ND_ADD,       // "+"
    ND_SUB,       // "-"
    ND_MUL,       // "*"
    ND_DIV,       // "/"
    ND_MOD,       // "%"
    // Bitwise
    ND_BITAND,    // "&"
    ND_BITOR,     // "|"
    ND_BITXOR,    // "^"
    ND_BITNOT,    // "~"
    // Shift
    ND_SHL,       // "<<"
    ND_SHR,       // ">>"
    // Assign
    ND_ASSIGN,    // "="
    ND_ADD_ASG,   // "+="
    ND_SUB_ASG,   // "-="
    ND_MUL_ASG,   // "*="
    ND_DIV_ASG,   // "/="
    ND_MOD_ASG,   // "%="
    ND_SHL_ASG,   // "<<="
    ND_SHR_ASG,   // ">>="
    ND_AND_ASG,   // "&="
    ND_OR_ASG,    // "|="
    ND_XOR_ASG,   // "^="
    // Logic
    ND_LOG_AND,   // "&&"
    ND_LOG_OR,    // "||"
    ND_LOG_NOT,   // "!"
    // Compare
    ND_EQ,        // "=="
    ND_NEQ,       // "!="
    ND_LT,        // "<"
    ND_LE,        // "<="
    ND_GT,        // ">"
    ND_GE,        // ">="
    // Inc and Dec
    ND_INC,       // "++"
    ND_DEC,       // "--"
    // Struct member assess
    ND_DOT,       // "."
    ND_ARROW,     // "->"
    // Others
    ND_FUNCALL,   // function call, left: FuncName, right: Args
    ND_INDEX,     // array assess, left ArrayName, right: Index
    ND_COMMA,     // ","
    ND_COND,      // "?", NodeInfo: condi, left, right
    ND_SIZEOF,    // "sizeof"

    // Pointers
    ND_DEREF,     // "*a"
    ND_ADDR,      // "&a"
    // NodeInfo: left

    ///////////
    // Key Stmt
    ///////////
    ND_IF,      // NodeInfo: condi, body, els
    ND_WHILE,   // NodeInfo: condi, body
    ND_DO,      // NodeInfo: body, condi
    ND_FOR,     // NodeInfo: init, condi, body, inc
    ND_SWITCH,  // NodeInfo: condi, body
    ND_CASE,    // NodeInfo: condi, body
    ND_DEFAULT, // NodeInfo: body
    ND_ASM,     // NodeInfo: body
    ND_RETURN,  // NodeInfo: body
    ND_BREAK,   // NodeInfo: (null)
    ND_CONTINUE,// NodeInfo: (null)

    ND_TYPE,    // NodeInfo: type
    ND_INILIST, // NodeInfo: init
    ND_DECL,    // NodeInfo: left(type), right(ident), init(Initializer)
    ND_CAST,    // NodeInfo: left(type), right(ident)

    ////////////
    // Leaf Node
    ////////////
    ND_NUM,     // NodeInfo: num
    ND_CH,      // NodeInfo: ch
    ND_STR,     // NodeInfo: str
    ND_IDENT,   // NodeInfo: name

    ND_TRUE,    // boolean literal true
    ND_FALSE,   // boolean literal false
    ND_NULL,    // null pointer literal

    ND_UNKNOWN
};

enum TypeKind {
    TY_VOID,
    TY_BOOL,
    TY_CHAR,
    TY_INT,
    TY_SHORT,
    TY_LONG,
    TY_STRUCT,
    TY_ENUM,
    TY_PTR,
    TY_ARY,
    TY_UNKNOWN
};

enum SignKind {
    SG_SIGNED,
    SG_UNSIGNED,
    SG_UNKNOWN     // not integer
};

struct Type {
    enum TypeKind kind;
    enum SignKind sign;
    int len;    // if array
    char* name; // if struct or enum
    struct Type* base; // if array or pointer
};

struct Node {
    enum NodeKind kind;
    struct Node* left;
    struct Node* right;

    struct Node* condi;
    struct Node* body;
    struct Node* els;
    struct Node* init;
    struct Node* inc;

    struct Type* type;  // ND_TYPE only

    long num;
    char ch;
    char* str;
    char* name;

    struct Node* next;
};

int Len(const char *str);
void Cpy(char *dst, const char *src);
void Cat(char *dst, const char *src);
int Cmp(const char *s1, const char *s2, const int len);
unsigned long Atoi(const char* str, int base);
void Itoa(char* buf, long data);

enum PunctKind getPunctKind(const char* str);
enum KeywordKind getKeywordKind(const char* str);

char* read_source(const char* path);
char* preprocessor(const char* vanilla);
struct Token* Lexer(const char* src);
struct Node* Parser(struct Token* head);
// void Sema(struct Node* root);
void Codegen(struct Node* root);


// debug
const char* getPunctText(enum PunctKind pk);
void printToken(struct Token* tok);
void debug_info_dumpSrc(const char* src);
void debug_info_dumpTokens(struct Token* head);
void debug_info_dumpAbstractSyntaxTree(struct Node* const root);

#endif
